/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.controller;

import cn.easyplatform.EasyPlatformWithLabelKeyException;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.request.BeginRequestMessage;
import cn.easyplatform.messages.vos.*;
import cn.easyplatform.messages.vos.h5.MessageVo;
import cn.easyplatform.spi.service.TaskService;
import cn.easyplatform.type.IResponseMessage;
import cn.easyplatform.web.contexts.Contexts;
import cn.easyplatform.web.layout.ILayoutManagerFactory;
import cn.easyplatform.web.layout.IMainTaskBuilder;
import cn.easyplatform.web.layout.LayoutManagerFactory;
import cn.easyplatform.web.listener.LogoutListener;
import cn.easyplatform.web.message.impl.MessageHandler;
import cn.easyplatform.web.service.ServiceLocator;
import cn.easyplatform.web.task.BackendException;
import cn.easyplatform.web.task.MainTaskSupport;
import cn.easyplatform.web.utils.CookieUtils;
import cn.easyplatform.web.utils.WebUtils;
import org.apache.commons.lang3.RandomUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zkoss.util.resource.Labels;
import org.zkoss.xel.util.SimpleResolver;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.metainfo.ComponentInfo;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zk.ui.util.Composer;
import org.zkoss.zk.ui.util.ComposerExt;
import org.zkoss.zul.*;

import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @Description:
 * @Since: 2.0.0 <br/>
 * @Date: Created in 2019/4/19 11:30
 * @Modified By:
 */
abstract class AbstractLayoutController implements Composer<Component>,
        ComposerExt<Component> {

    protected final static Logger log = LoggerFactory.getLogger(AbstractLayoutController.class);

    private Map<String, MessageHandler> messageHandlers;

    @Override
    public ComponentInfo doBeforeCompose(Page page, Component component,
                                         ComponentInfo componentinfo) throws Exception {
        EnvVo env = Contexts.getEnv();
        if (env == null) {
            Executions.sendRedirect("/login.go");
        } else {
            LoginVo user = Contexts.getUser();
            if (user == null) {
                if (Contexts.getEnv() != null)
                    Contexts.getEnv().setMainPage(null);
                Executions.sendRedirect("/login.go");
            } else {
                page.setTitle(env.getTitle());
                Map<String, Object> vars = new HashMap<String, Object>();
                vars.put("$easyplatform", this);
                page.addVariableResolver(new SimpleResolver(vars));
                if (!Strings.isBlank(user.getTooltip()))
                    Clients.alert(user.getTooltip(), Labels.getLabel("message.dialg.title.warning"), "EXCLAMATION");
                Clients.confirmClose(Labels.getLabel("confirm.close"));
                CookieUtils.addCookie((HttpServletResponse) Executions.getCurrent().getNativeResponse(), "zk-project", env.getProjectId(), null);
                messageHandlers = new HashMap<>();
            }
        }
        return componentinfo;
    }

    @Override
    public void doBeforeComposeChildren(Component comp) throws Exception {
        // ConventionWires.wireController(comp, this);
    }

    @Override
    public boolean doCatch(Throwable throwable) throws Exception {
        return false;
    }

    @Override
    public void doFinally() throws Exception {
    }

    @Override
    public void doAfterCompose(Component comp) throws Exception {
        init(comp);
    }

    /**
     * 初始化
     *
     * @param comp
     */
    protected abstract void init(Component comp);

    /**
     * 登出
     */
    public void logout() {
        LogoutListener.logout(Contexts.getEnv());
    }

    /**
     * 消息
     *
     * @param comp
     */
    public void message(Component comp) {
        messageHandlers.put(MessageVo.TYPE_NOTICE, new MessageHandler(MessageVo.TYPE_NOTICE, comp));
    }

    /**
     * 消息
     *
     * @param comp
     */
    public void message(Component comp, String type) {
        messageHandlers.put(type, new MessageHandler(type, comp));
    }

    /**
     * 待办事项
     *
     * @param comp
     */
    public void bpm(Component comp) {
        new MessageHandler(MessageVo.TYPE_TASK, comp);
    }

    /**
     * @param id
     */
    public void bpm(Object id) {
        Component container = Executions.getCurrent().getDesktop().getFirstPage().getFellowIfAny("gv5container");
        if (container == null)
            container = Executions.getCurrent().getDesktop().getFirstPage().getFirstRoot();
        WebUtils.doBpm(container, id);
    }

    /**
     * 消息数减1
     */
    public void reduce() {
        reduce(MessageVo.TYPE_NOTICE);
    }

    /**
     * 消息数减1
     */
    public void reduce(String type) {
        MessageHandler messageHandler = messageHandlers.get(type);
        if (messageHandler != null)
            messageHandler.reduce();
    }

    /**
     * 系统设置
     *
     * @param c
     */
    public void setting(Component c) {
        Executions.createComponents("~./pages/settings.zul", null, null);
    }

    /**
     * 改变密码
     *
     * @param c
     */
    public void changepwd(Component c) {
        Executions.createComponents("~./pages/password.zul", null, null);
    }

    /**
     * 当前用户
     *
     * @return
     */
    public LoginVo getUser() {
        return Contexts.getUser();
    }


    /**
     * 设定用户
     *
     * @param c
     */
    public void user(Component c) {
        LoginVo user = Contexts.getUser();
        if (user == null) {
            if (Contexts.getEnv() != null)
                Contexts.getEnv().setMainPage(null);
            Executions.sendRedirect("/login.go");
        } else {
            if (c instanceof Label) {
                Label header = (Label) c;
                if (Strings.isBlank(header.getValue()))
                    header.setValue(user.getId());
            } else if (c instanceof A) {
                A a = (A) c;
                if (Strings.isBlank(a.getLabel()))
                    a.setLabel(user.getId());
            }
        }
    }


    /**
     * 调试
     *
     * @param comp
     */
    public void debug(Component comp) {
        ILayoutManagerFactory lmf = LayoutManagerFactory.createLayoutManager();
        lmf.getDebugBuilder().build(comp);
    }

    /**
     * 主页功能
     *
     * @param comp
     * @param taskId
     */
    public void task(Component comp, String taskId) {
        task(comp, taskId, true, null);
    }

    /**
     * 主页功能
     *
     * @param comp
     * @param taskId
     * @param title  是否要显示标题
     */
    public void task(Component comp, String taskId, boolean title) {
        task(comp, taskId, title, null);
    }

    /**
     * 主页功能
     *
     * @param comp
     * @param taskId
     * @param roles  符合指定的角色才可能显示
     */
    public void task(Component comp, String taskId, String roles) {
        task(comp, taskId, true, roles);
    }

    /**
     * 主页功能
     *
     * @param comp
     * @param taskId
     * @param title  是否要显示标题
     * @param roles  符合指定的角色才可能显示
     */
    public void task(final Component comp, final String taskId,
                     final boolean title, final String roles) {
        // 延迟加载主页功能
        Timer timer = new Timer(RandomUtils.nextInt(10, 200));
        timer.setRepeats(false);
        timer.addEventListener(Events.ON_TIMER, new EventListener<Event>() {
            @Override
            public void onEvent(Event evt) throws Exception {
                try {
                    AuthorizationVo av = (AuthorizationVo) comp.getDesktop().getSession()
                            .getAttribute(Contexts.PLATFORM_USER_AUTHORIZATION);
                    if (av == null) {
                        if (Contexts.getEnv() != null)
                            Contexts.getEnv().setMainPage(null);
                        Executions.sendRedirect("/login.go");
                    } else {
                        String defaultRoleId = "default";
                        if (!av.getRoles().isEmpty())
                            defaultRoleId = av.getRoles().get(0).getId();
                        doTask(comp, taskId, title, roles, av, defaultRoleId);
                    }
                } finally {
                    evt.getTarget().detach();
                }
            }
        });
        timer.setRunning(true);
        comp.getPage().getLastRoot().appendChild(timer);
    }


    /**
     * 执行功能
     *
     * @param comp
     * @param taskId
     * @param title
     * @param roles
     * @param av
     * @param defaultRoleId
     */
    protected void doTask(Component comp, String taskId, boolean title,
                          String roles, AuthorizationVo av, String defaultRoleId) {
        if (roles != null) {
            String[] runroles = roles.split(",");
            boolean isRole = false;
            for (String role : runroles) {
                for (RoleVo rv : av.getRoles()) {
                    if (rv.getId().equals(role)) {
                        defaultRoleId = role;
                        isRole = true;
                        break;
                    }
                }
                if (isRole)
                    break;
            }
            if (!isRole) {
                comp.detach();
                comp = null;
                return;
            }
        }
        Component container = comp;
        if (comp instanceof Panel) {
            container = new Panelchildren();
            comp.appendChild(container);
        }
        TaskService mtc = ServiceLocator
                .lookup(TaskService.class);
        TaskVo tv = new TaskVo(taskId);
        tv.setRoleId(defaultRoleId);
        BeginRequestMessage req = new BeginRequestMessage(tv);
        IResponseMessage<?> resp = mtc.begin(req);
        if (resp.isSuccess()) {
            MainTaskSupport builder = null;
            try {
                AbstractPageVo pv = (AbstractPageVo) resp.getBody();
                if (title && comp instanceof Panel)
                    ((Panel) comp).setTitle(pv.getTitile());
                builder = (MainTaskSupport) LayoutManagerFactory
                        .createLayoutManager()
                        .getMainTaskBuilder(container,
                                taskId, pv);
                ((IMainTaskBuilder) builder).build();
            } catch (EasyPlatformWithLabelKeyException ex) {
                builder.close(false);
                showError(container,
                        Labels.getLabel(ex.getMessage(), ex.getArgs()));
            } catch (BackendException ex) {
                builder.close(false);
                showError(container, ex.getMsg().getCode() + ":"
                        + ex.getMsg().getBody());
            } catch (Exception ex) {
                builder.close(false);
                showError(container,
                        ex.getMessage());
            }
        } else {
            showError(container, resp.getCode() + ":" + resp.getBody());
        }
    }

    /**
     * 显示执行功能错误信息
     *
     * @param parent
     * @param msg
     */
    private void showError(Component parent, String msg) {
        Div div = new Div();
        div.setClass("alert alert-danger");
        div.setStyle("margin-top:30px");
        Label label = new Label();
        label.setValue(msg);
        label.setParent(div);
        div.setParent(parent);
    }
}
